<?php

namespace Wonfu\Core;

use Doctrine\Common\Annotations\AnnotationReader;
use Wonfu\Core\Annotations\Valid;
use Wonfu\Core\Route\RouteInfo;
use Wonfu\Http\ServerRequest;

class ParamsResolver
{

    /**
     * @var \ReflectionMethod
     */
    private $methodReflection;


    /**
     * @var ServerRequest
     */
    private $request;

    /**
     * @var RouteInfo
     */
    private $routeInfo;

    /**
     * @param \ReflectionMethod $methodReflection
     * @
     */
    public function __construct(\ReflectionMethod $methodReflection, ServerRequest $serverRequest, RouteInfo $routeInfo)
    {
        $this->methodReflection = $methodReflection;
        $this->request = $serverRequest;
        $this->routeInfo = $routeInfo;
    }


    public function resolve(): array
    {
        $reader = new AnnotationReader();
        $methodReflection = $this->methodReflection;
        $methodAnnotation = $reader->getMethodAnnotation($methodReflection, Valid::class);
        if ($methodAnnotation != null) {
            return $this->withValidAnnotation($methodAnnotation);
        }
        $parameters = $this->routeInfo->getParameter();
        if ($parameters) {
            return $this->withParameter($parameters);
        }
        return [];
    }


    /**
     * @param Valid $methodAnnotation
     * @return mixed|object|void
     * @throws \JsonMapper_Exception
     * @throws \ReflectionException
     */
    private function withValidAnnotation($methodAnnotation)
    {
        $params = [];
        $validParamsIn = $methodAnnotation->validParamsIn;
        $data = null;
        switch ($validParamsIn) {
            case Valid::IN_QUERY:
                $data = $this->handWithValidQuery();
                break;
            case Valid::IN_REQUEST_BODY:
                $validContentType = $methodAnnotation->contentType;
                $data = $this->handWithValidRequest($validContentType);
                break;
        }
        foreach ($this->methodReflection->getParameters() as $parameter) {
            if ($parameter->getName() == $methodAnnotation->paramKey) {
                $paramClass = $parameter->getType()->getName();
                $mapper = new \JsonMapper();
                $mapper->bIgnoreVisibility = true;
                $jsonObj = json_decode($data);
                if (empty($jsonObj)) {
                    throw new ParamsResolverException();
                }
                $vars = $mapper->map($jsonObj,
                    (new \ReflectionClass($paramClass))->newInstance());
                $params[$parameter->getPosition()] = $vars;
            }
        }
        return $params;
    }

    private function handWithValidQuery()
    {
        $data = $this->request->getQueryParams();
        return json_encode($data);
    }

    private function handWithValidRequest($validContentType)
    {
        $data = null;
        $contentType = $this->request->getContentType();
        if (strpos($validContentType, $contentType) !== false) {
            switch ($contentType) {
                case 'json':
                    $data = $this->request->getContent();
                    break;
            }
        }
        return $data;

    }

    private function toObtainParameters($paramsIn)
    {
        //TODO parse parameters
    }

    /**
     * @param array $parameters
     */
    private function withParameter($parameters)
    {
        $params = [];
        foreach ($parameters as $name => $value) {
            foreach ($this->methodReflection->getParameters() as $parameter) {
                if ($parameter->getName() == $name) {
                    $params[$parameter->getPosition()] = $value;
                }
            }
        }
        return $params;
    }

}